package com.czk.classreader.counttime;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;

import static org.objectweb.asm.Opcodes.*;

/**
 * 计算多个方法调用计时综合
 *
 * @Author:ChenZhangKun
 * @Date: 2021/8/23 16:20
 */
public class MethodTimerVisitor extends ClassVisitor {
    private String owner;
    private boolean isInterface;

    public MethodTimerVisitor(int api, ClassVisitor classVisitor) {
        super(api, classVisitor);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        // 当前类的名字
        owner = name;
        // 相同二进制进行与操作
        isInterface = (access & ACC_INTERFACE) != 0;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
        if (!isInterface && methodVisitor != null && !"<init>".equals(name) && !"<cinit>".equals(name)) {
            // 是否是本地方法
            boolean isNativeMethod = (access & ACC_NATIVE) != 0;
            // 是否是抽象方法
            boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;
            if (!isNativeMethod && !isAbstractMethod) {
                methodVisitor = new MethodTimerAdapter(api, methodVisitor, owner);
            }
        }
        return methodVisitor;
    }

    @Override
    public void visitEnd() {
        // 是否是接口
        if (!isInterface) {
            // 添加timer字段
            FieldVisitor fv = super.visitField(ACC_PUBLIC | ACC_STATIC, "timer", "J", null, null);
            if (fv != null) {
                fv.visitEnd();
            }
        }
        super.visitEnd();
    }

    private class MethodTimerAdapter extends MethodVisitor {
        private final String owner;

        public MethodTimerAdapter(int api, MethodVisitor methodVisitor, String owner) {
            super(api, methodVisitor);
            this.owner = owner;
        }

        /**
         * 进入方法时执行的相关代码
         */
        @Override
        public void visitCode() {
            // 首先，处理自己的代码逻辑
            super.visitFieldInsn(GETSTATIC, owner, "timer", "J");
            super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            super.visitInsn(LSUB);
            super.visitFieldInsn(PUTSTATIC, owner, "timer", "J");

            // 其次，调用父类的方法实现
            super.visitCode();
        }

        /**
         * 方法退出时执行的逻辑
         *
         * @param opcode
         */
        @Override
        public void visitInsn(int opcode) {
            // 首先，处理自己的代码逻辑
            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
                super.visitFieldInsn(GETSTATIC, owner, "timer", "J");
                super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                super.visitInsn(LADD);
                super.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
            }

            // 其次，调用父类的方法实现
            super.visitInsn(opcode);
        }

    }
}
